import { OAuthProviderFactory, type ProviderType } from '../oauth2/oauth-factory';
import type { OAuthConfig } from '../oauth2/base-provider';
import { db } from '../config/database';
import type { User, Account } from '@prisma/client';
import logger from '../utils/logger';
import jwt from 'jsonwebtoken';

// OAuth用户信息类型
export interface OAuthUserInfo {
  id: string;
  email: string;
  name: string;
  avatar?: string;
}

// OAuth认证结果类型
export interface OAuthAuthResult {
  user: User;
  account: Account;
  isNewUser: boolean;
  tokens: any;
}

// 状态参数类型
export interface StateParams {
  provider: ProviderType;
  timestamp: number;
  userId?: string; // 用于账号绑定
  action: 'login' | 'link';
}

// JWT令牌类型
export interface JWTTokens {
  accessToken: string;
  refreshToken: string;
}

export class OAuthService {
  private static instance: OAuthService;
  private initialized = false;

  private constructor() {
  }

  /**
   * 获取单例实例
   */
  static getInstance(): OAuthService {
    if (!OAuthService.instance) {
      OAuthService.instance = new OAuthService();
    }
    return OAuthService.instance;
  }

  /**
   * 初始化OAuth服务
   */
  async initialize(): Promise<void> {
    if (this.initialized) {
      return;
    }

    try {
      // 初始化Google OAuth提供商
      if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) {
        // 构建正确的回调URL
        const baseUrl = process.env.SITE_DOMAIN;
        const redirectUri = process.env.GOOGLE_REDIRECT_URI || `${baseUrl}/api/oauth/google/callback`;

        const googleConfig: OAuthConfig = {
          clientId: process.env.GOOGLE_CLIENT_ID,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET,
          redirectUri: redirectUri,
          scopes: ['email', 'profile']
        };

        logger.info(`Google OAuth配置: redirectUri=${redirectUri}`);
        OAuthProviderFactory.createProvider('GOOGLE', googleConfig);
        logger.info('Google OAuth提供商初始化成功');
      }

      // 初始化GitHub OAuth提供商
      if (process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET) {
        const baseUrl = process.env.SITE_DOMAIN;
        const redirectUri = process.env.GITHUB_REDIRECT_URI || `${baseUrl}/api/oauth/github/callback`;

        const githubConfig: OAuthConfig = {
          clientId: process.env.GITHUB_CLIENT_ID,
          clientSecret: process.env.GITHUB_CLIENT_SECRET,
          redirectUri: redirectUri,
          scopes: ['user:email', 'read:user']
        };

        OAuthProviderFactory.createProvider('GITHUB', githubConfig);
        logger.info(`GitHub OAuth提供商初始化成功: redirectUri=${redirectUri}`);
      } else {
        logger.warn('GitHub OAuth 环境变量未配置，跳过初始化');
      }

      // 初始化Twitter OAuth提供商
      if (process.env.X_CLIENT_ID && process.env.X_CLIENT_SECRET) {
        const baseUrl = process.env.SITE_DOMAIN;
        const redirectUri = process.env.X_REDIRECT_URI || `${baseUrl}/api/oauth/twitter/callback`;

        const twitterConfig: OAuthConfig = {
          clientId: process.env.X_CLIENT_ID,
          clientSecret: process.env.X_CLIENT_SECRET,
          redirectUri: redirectUri,
          scopes: ['tweet.read', 'users.read', 'offline.access'] // Twitter API v2 推荐的基本scopes
        };

        OAuthProviderFactory.createProvider('TWITTER', twitterConfig);
        logger.info(`Twitter OAuth提供商初始化成功: redirectUri=${redirectUri}`);
      } else {
        logger.warn('Twitter OAuth 环境变量未配置，跳过初始化');
      }


      this.initialized = true;
      logger.info('OAuth服务初始化成功');
    } catch (error) {
      logger.error('OAuth服务初始化失败:', error);
      throw error;
    }
  }

  /**
   * 生成OAuth授权URL
   */
  async generateAuthUrl(provider: string, action: string = 'login', userId?: string): Promise<string> {
    try {
      // 验证提供商
      if (!provider || !['google', 'github', 'twitter'].includes(provider.toLowerCase())) {
        throw new Error('不支持的OAuth提供商。支持的提供商：google, github, twitter');
      }

      const providerType = provider.toUpperCase() as ProviderType;

      if (!OAuthProviderFactory.isProviderRegistered(providerType)) {
        throw new Error(`${provider} OAuth未配置`);
      }

      const oauthProvider = OAuthProviderFactory.getProvider(providerType);

      // 生成状态参数
      const stateParams: StateParams = {
        provider: providerType,
        timestamp: Date.now(),
        action: action as 'login' | 'link',
        userId: userId
      };

      const stateString = JSON.stringify(stateParams);
      const state = Buffer.from(stateString).toString('base64url');

      const authUrl = oauthProvider.generateAuthUrl(state);

      logger.info(`生成${provider} OAuth授权URL成功,${authUrl}`);
      return authUrl;
    } catch (error) {
      logger.error('生成OAuth授权URL失败:', error);
      throw error;
    }
  }

  /**
   * 处理OAuth回调
   */
  async handleCallback(provider: string, code: string, state: string): Promise<{
    user: User;
    tokens: JWTTokens;
    isNewUser: boolean;
    redirectUrl: string;
  }> {
    try {
      const providerType = provider.toUpperCase() as ProviderType;

      // 验证状态参数
      const stateParams = this.decodeAndValidateState(state, providerType);

      const oauthProvider = OAuthProviderFactory.getProvider(providerType);

      // 获取访问令牌和用户信息
      const tokens: any = await oauthProvider.exchangeCodeForTokens(code);
      const userInfo = await oauthProvider.getUserInfo(tokens.access_token);
      const processedData = oauthProvider.processUserData(userInfo) as unknown as OAuthUserInfo;

      // 处理用户数据
      const result = await this.processOAuthUser(providerType, processedData, tokens);

      // 生成JWT令牌
      const jwtTokens = await this.generateJWTTokens(result.user);

      // 存储会话到Redis
      await this.storeUserSession(result.user.id, jwtTokens.refreshToken);

      // 生成重定向URL
      const redirectUrl = this.generateRedirectUrl(stateParams, provider, jwtTokens, result.isNewUser);

      logger.info(`${provider} OAuth回调处理成功: ${result.user.email}`);

      return {
        user: result.user,
        tokens: jwtTokens,
        isNewUser: result.isNewUser,
        redirectUrl
      };
    } catch (error) {
      logger.error(`OAuth ${provider} 回调处理失败:`, error);
      throw error;
    }
  }

  /**
   * 获取用户的OAuth账户列表
   */
  async getUserOAuthAccounts(userId: string): Promise<Partial<Account>[]> {
    try {
      const prisma = await db.prisma();

      const accounts = await prisma.account.findMany({
        where: {
          userId: userId
        },
        select: {
          id: true,
          provider: true,
          createdAt: true
        }
      });

      return accounts;
    } catch (error) {
      logger.error('获取用户OAuth账户失败:', error);
      throw error;
    }
  }

  /**
   * 解绑OAuth账户
   */
  async unlinkOAuthAccount(userId: string, provider: string): Promise<void> {
    try {
      const prisma = await db.prisma();

      const account = await prisma.account.findFirst({
        where: {
          userId: userId,
          provider: provider.toUpperCase() as ProviderType
        }
      });

      if (!account) {
        throw new Error('OAuth账户未找到');
      }

      await prisma.account.delete({
        where: { id: account.id }
      });

      logger.info(`用户解绑${provider}账户成功: ${userId}`);
    } catch (error) {
      logger.error('解绑OAuth账户失败:', error);
      throw error;
    }
  }

  /**
   * 绑定OAuth账户到现有用户
   */
  async linkOAuthAccount(userId: string, provider: string, code: string, state: string): Promise<void> {
    try {
      const providerType = provider.toUpperCase() as ProviderType;

      // 验证状态参数
      const stateParams = this.decodeAndValidateState(state, providerType);

      if (stateParams.action !== 'link' || stateParams.userId !== userId) {
        throw new Error('状态参数不匹配');
      }

      const oauthProvider = OAuthProviderFactory.getProvider(providerType);

      // 获取访问令牌和用户信息
      const tokens: any = await oauthProvider.exchangeCodeForTokens(code);
      const userInfo: any = await oauthProvider.getUserInfo(tokens.access_token);
      const processedData = oauthProvider.processUserData(userInfo) as unknown as OAuthUserInfo;

      await this.linkAccountToUser(userId, providerType, processedData, tokens);

      logger.info(`用户绑定${provider}账户成功: ${userId}`);
    } catch (error) {
      logger.error(`绑定${provider}账户失败:`, error);
      throw error;
    }
  }

  // ============ 私有方法 ============

  /**
   * 解码和验证状态参数
   */
  private decodeAndValidateState(state: string, expectedProvider: ProviderType): StateParams {
    let stateParams: StateParams;

    try {
      const stateString = Buffer.from(state, 'base64url').toString();
      stateParams = JSON.parse(stateString);
    } catch {
      throw new Error('无效的状态参数');
    }

    // 检查状态是否过期（10分钟）
    if (Date.now() - stateParams.timestamp > 10 * 60 * 1000) {
      throw new Error('状态参数已过期');
    }

    if (stateParams.provider !== expectedProvider) {
      throw new Error('提供商不匹配');
    }

    return stateParams;
  }

  /**
   * 处理OAuth用户数据
   */
  private async processOAuthUser(provider: ProviderType, userData: OAuthUserInfo, tokens: any): Promise<OAuthAuthResult> {
    try {
      const prisma = await db.prisma();

      // 查找现有账户
      let account = await prisma.account.findFirst({
        where: {
          provider: provider,
          providerAccountId: userData.id
        },
        include: {
          user: true
        }
      });

      let user: User;
      let isNewUser = false;
      let finalAccount: Account;

      if (account) {
        // 现有账户，更新令牌，更新用户信息
        user = account.user;
        const updatedAccount = await prisma.account.update({
          where: { id: account.id },
          data: {
            accessToken: tokens.access_token,
            refreshToken: tokens.refresh_token,
            expiresAt: tokens.expires_in ? new Date(Date.now() + tokens.expires_in * 1000) : undefined,
            providerUsername: userData.name,
            providerEmail: userData.email,
            providerAvatar: userData.avatar,
          }
        });
        finalAccount = updatedAccount as Account;
      } else {
        // 检查是否有相同邮箱的用户
        const existingUser = await prisma.user.findUnique({
          where: { email: userData.email }
        });

        if (existingUser) {
          // 为现有用户创建新的OAuth账户
          user = existingUser;
          const newAccount = await prisma.account.create({
            data: {
              userId: user.id,
              provider: provider,
              providerAccountId: userData.id,
              providerAvatar: userData.avatar,
              providerEmail: userData.email,
              providerUsername: userData.name,
              accessToken: tokens.access_token,
              refreshToken: tokens.refresh_token,
              expiresAt: tokens.expires_in ? new Date(Date.now() + tokens.expires_in * 1000) : undefined
            }
          });
          finalAccount = newAccount as Account;
        } else {
          // 创建新用户和账户
          const result = await prisma.user.create({
            data: {
              email: userData.email,
              username: userData.name,
              avatar: userData.avatar,
              emailVerified: true,
              plan: 'FREE',
              credits: 10,
              accounts: {
                create: {
                  provider: provider,
                  providerAccountId: userData.id,
                  accessToken: tokens.access_token,
                  refreshToken: tokens.refresh_token,
                  expiresAt: tokens.expires_in ? new Date(Date.now() + tokens.expires_in * 1000) : undefined,
                  providerUsername: userData.name,
                  providerEmail: userData.email,
                  providerAvatar: userData.avatar
                }
              }
            },
            include: {
              accounts: true
            }
          });

          user = result;
          finalAccount = result.accounts[0] as Account;
          isNewUser = true;

          logger.info(`新用户通过${provider}注册: ${user.email}`);
        }
      }

      return { user, account: finalAccount, isNewUser, tokens };
    } catch (error) {
      logger.error(`处理${provider}用户数据失败:`, error);
      throw error;
    }
  }

  /**
   * 绑定账户到现有用户
   */
  private async linkAccountToUser(userId: string, provider: ProviderType, userData: OAuthUserInfo, tokens: any): Promise<void> {
    const prisma = await db.prisma();

    // 检查用户是否存在
    const user = await prisma.user.findUnique({
      where: { id: userId }
    });

    if (!user) {
      throw new Error('用户不存在');
    }

    // 检查是否已经绑定了该提供商的账户
    const existingAccount = await prisma.account.findFirst({
      where: {
        userId: userId,
        provider: provider
      }
    });

    if (existingAccount) {
      throw new Error(`${provider}账户已绑定`);
    }

    // 检查该OAuth账户是否已被其他用户绑定
    const existingProviderAccount = await prisma.account.findFirst({
      where: {
        provider: provider,
        providerAccountId: userData.id
      }
    });

    if (existingProviderAccount) {
      throw new Error(`${provider}账户已被其他用户绑定`);
    }

    // 创建新的账户绑定
    await prisma.account.create({
      data: {
        userId: userId,
        provider: provider,
        providerAccountId: userData.id,
        accessToken: tokens.access_token,
        refreshToken: tokens.refresh_token,
        expiresAt: tokens.expires_in ? new Date(Date.now() + tokens.expires_in * 1000) : undefined
      }
    });
  }

  /**
   * 生成JWT令牌
   */
  private async generateJWTTokens(user: User): Promise<JWTTokens> {
    const accessToken = jwt.sign(
      { userId: user.id, email: user.email },
      process.env.JWT_SECRET!,
      { expiresIn: '15m' }
    );

    const refreshToken = jwt.sign(
      { userId: user.id, type: 'refresh' },
      process.env.JWT_REFRESH_SECRET!,
      { expiresIn: '7d' }
    );

    return { accessToken, refreshToken };
  }

  /**
   * 存储用户会话
   */
  private async storeUserSession(userId: string, refreshToken: string): Promise<void> {
    const redis = await db.redis();
    await redis.setex(`session:${userId}`, 7 * 24 * 60 * 60, refreshToken);
  }

  /**
   * 生成重定向URL
   */
  private generateRedirectUrl(stateParams: StateParams, provider: string, tokens: JWTTokens, isNewUser: boolean): string {
    const baseUrl = process.env.SITE_DOMAIN || 'http://localhost:3000';

    if (stateParams.action === 'link') {
      return `${baseUrl}/settings/account?linked=${provider}`;
    } else {
      return `${baseUrl}/auth/success?token=${tokens.accessToken}&refresh=${tokens.refreshToken}&newUser=${isNewUser}`;
    }
  }
}

export default OAuthService.getInstance(); 